package com.example.handler;

import android.graphics.Bitmap;
import android.util.Log;

import com.example.android_toolkit.AndroidDevice;
import com.example.app1.MainActivity;
import com.example.event.Event;
import com.example.event.OnClickEvent;
import com.example.event.OnLongClickEvent;
import com.example.event.OnTouchEvent;
import com.example.logging.Logger;
import com.example.messaging.EventCMessage;
import com.example.messaging.GUIInitSMessage;
import com.example.messaging.InitCMessage;
import com.example.messaging.InitSMessage;
import com.example.messaging.Message;
import com.example.networking.ClientNetworkAgentReceiver;
import com.example.networking.ClientNetworkAgentSender;
import com.example.networking.Session;
import com.example.xml.XMLHandler;

public class ClientHandler {

	private MainActivity context;
	private XMLHandler xmlHandler;
	private Session session;
	private static ClientHandler instance;
	private Bitmap image;

	private ClientNetworkAgentReceiver agentReceiver;
	private ClientNetworkAgentSender agentSender;

	private ClientHandler(MainActivity context) {
		this.context = context;
	}

	public static ClientHandler getInstance(MainActivity context) {
		if (instance == null) {
			instance = new ClientHandler(context);
			instance.xmlHandler = XMLHandler.getInstance();
			instance.agentSender = ClientNetworkAgentSender.getInstance();
			instance.agentReceiver = ClientNetworkAgentReceiver.getInstance();
		}
		return instance;
	}

	public static ClientHandler getInstance() {
		if (instance == null) {
			Log.e(Logger.ERR,
					"Call to second constructor in ClientHandler is needed!");
			throw new RuntimeException(
					"Call to second constructor in ClientHandler is needed!");
		}
		return instance;
	}

	public Bitmap getImage() {
		return image;
	}

	public void receive(Message message) {

		// receive response from server regarding the connection init
		if (message instanceof InitSMessage) {
			Log.i(Logger.DEBUG, "InitSMessage received.");

			InitSMessage response = (InitSMessage) message;

			// check, if the answer code is 200 (OK)
			if (!response.getServerAnswer().equals("200")) {
				Log.e(Logger.ERR, "Server refused to establish connection!");
				throw new RuntimeException(
						"Server refused to establish connection!");
			}

			// setup session with received Session id
			// any future request and response message for server contains
			// received
			// session information
			session = response.getSession();
			Log.i(Logger.DEBUG, "Client is connected!");
			// from now on, the connection is set!
			return;
		}

		// receive initial UI layout from server
		if (message instanceof GUIInitSMessage) {
			Log.i(Logger.DEBUG, "GUIInitSMessage received.");

			GUIInitSMessage guiMessage = (GUIInitSMessage) message;

			image = guiMessage.getRenderedComponent();

			synchronized (context.lock) {
				context.lock.notify();
			}

			Log.i(Logger.DEBUG, "finished!");

			return;
		}
	}

	public void init() {

		// init daemons, that take care of receiving and sending messages to
		// server
		initDaemons();

		if (agentSender == null) {
			Log.e(Logger.ERR, "agentSender nesmi byt null!");
		}

		// establish the connection with server
		AndroidDevice device = new AndroidDevice(context);

		InitCMessage initMessage = new InitCMessage();
		initMessage.setDevice(device);

		xmlHandler.send(initMessage);

	}

	private void initDaemons() {

		Thread t1 = new Thread(agentReceiver);
		t1.setDaemon(true);

		Thread t2 = new Thread(agentSender);
		t2.setDaemon(true);

		// start daemon threads
		t1.start();
		t2.start();

		// TODO replace with something, that waits, until both threads t1 and t2
		// are ready to perform
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	// ///////////////////////////////////////////////////////////////

	// TODO nenaslo by se elegantnejsi reseni, napriklad vlastni EventHandler???
	private int[] point;

	public void eventOccured(Event event) {

		// TODO otestovat, zda vyvolany event odpovida tomu, ktery chce server
		// prijimat

		// sestavit message a odeslat

		// update current event location
		if (event.getPoint() != null) {
			point = event.getPoint();
		}

		if (event instanceof OnLongClickEvent) { // OnLongClickEvent only

			Log.i(Logger.DEBUG, "onLongClickEvent message sent.");

			Message message = new EventCMessage();
			((EventCMessage) message).setEventType("onLongClick");
			((EventCMessage) message).setPoint(point);

			xmlHandler.send(message);

			return;
		}

		if (event instanceof OnTouchEvent) { // OnTouchEvent only!

			OnTouchEvent myEvent = (OnTouchEvent) event;
			
			Message message = null; 

			if (myEvent.getAction().equals("ACTION_UP")) {
				
				Log.i(Logger.DEBUG, "onTouchEvent UP message sent.");

				message = new EventCMessage();
				((EventCMessage) message).setEventType("onTouch");
				((EventCMessage) message).setPointList(myEvent.getPointList());
				((EventCMessage) message).setAction(myEvent.getAction());
				
			} else if(myEvent.getAction().equals("ACTION_MOVE")) {
				
				Log.i(Logger.DEBUG, "onTouchEvent MOVE message sent.");

				message = new EventCMessage();
				((EventCMessage) message).setEventType("onTouch");
				((EventCMessage) message).setPoint(point);
				((EventCMessage) message).setAction(myEvent.getAction());
				
			} else if(myEvent.getAction().equals("ACTION_DOWN")) {
				
				Log.i(Logger.DEBUG, "onTouchEvent DOWN message sent.");

				message = new EventCMessage();
				((EventCMessage) message).setEventType("onTouch");
				((EventCMessage) message).setPoint(point);
				((EventCMessage) message).setAction(myEvent.getAction());
				
			} else {
				throw new RuntimeException("Invalid action in ClientHandler occured!");
			}
			
			xmlHandler.send(message);

			return;
		}

		if (event instanceof OnClickEvent) { // OnClickEvent only!

			Log.i(Logger.DEBUG, "onClickEvent message sent.");

			Message message = new EventCMessage();
			((EventCMessage) message).setEventType("onClick");
			((EventCMessage) message).setPoint(point);

			// Log.i(Logger.DEBUG, message.getXML());

			xmlHandler.send(message);

			return;
		}
	}
}
